package st.learnopengl.apps.basic

import glm_.glm
import glm_.mat4x4.Mat4
import glm_.vec3.Vec3
import glm_.vec4.Vec4
import gln.*
import gln.draw.DrawMode
import gln.draw.glDrawArrays
import gln.glf.VertexAttribute
import gln.identifiers.GlBuffer
import gln.identifiers.GlProgram
import gln.identifiers.GlTexture
import gln.uniform.glUniform
import gln.vertexArray.GlVertexArray
import st.learnopengl.common.App
import st.learnopengl.common.GLUT
import org.lwjgl.glfw.GLFW.glfwGetTime
import org.lwjgl.opengl.GL11C.*
import org.lwjgl.opengl.GL13
import org.lwjgl.opengl.GL13C
import kotlin.math.cos
import kotlin.math.sin

class Camera3D(width: Int, height: Int, title: String)
    : App(width, height, title) {

    private var program: GlProgram? = null
    private var texture1: GlTexture? = null
    private var texture2: GlTexture? = null
    private var vao: GlVertexArray? = null
    private var vbo: GlBuffer? = null
    private lateinit var aPosAttr: VertexAttribute
    private lateinit var aUVAttr: VertexAttribute

    private var texture1Location: Int = -1
    private var texture2Location: Int = -1
    private var mvpLocation: Int = -1

    private val cubicPositions = listOf(
        Vec3( 0.0f,  0.0f,  0.0f),
        Vec3( 2.0f,  5.0f, -15.0f),
        Vec3(-1.5f, -2.2f, -2.5f),
        Vec3(-3.8f, -2.0f, -12.3f),
        Vec3 (2.4f, -0.4f, -3.5f),
        Vec3(-1.7f,  3.0f, -7.5f),
        Vec3( 1.3f, -2.0f, -2.5f),
        Vec3( 1.5f,  2.0f, -2.5f),
        Vec3( 1.5f,  0.2f, -1.5f),
        Vec3(-1.3f,  1.0f, -1.5f)
    )

    override fun onInit() {
        program = GLUT.createProgram("shaders/camera3d/vertexShader.glsl"
            , "shaders/camera3d/fragmentShader.glsl")
        program!!.apply {
            texture1Location = getUniformLocation("texture1")
            texture2Location = getUniformLocation("texture2")
            mvpLocation = getUniformLocation("mvp")
        }

        vao = GlVertexArray.gen()
        vao!!.bound {
            vbo = GlBuffer.gen()
            vbo!!.bound(BufferTarget.ARRAY) {
                var vertexs = floatArrayOf(
                    // positions          // texture coords
                    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
                    0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
                    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
                    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
                    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
                    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

                    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
                    0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
                    0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
                    0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
                    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
                    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

                    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
                    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
                    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
                    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
                    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
                    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

                    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
                    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
                    0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
                    0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
                    0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
                    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

                    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
                    0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
                    0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
                    0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
                    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
                    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

                    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
                    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
                    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
                    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
                    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
                    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
                )

                data(vertexs, Usage.STATIC_DRAW)
            }
        }

        aPosAttr = VertexAttribute(0, 3, VertexAttrType.FLOAT, false, 5 * 4, 0)
        aUVAttr = VertexAttribute(1, 2, VertexAttrType.FLOAT, false, 5 * 4, 3 * 4)

        texture1 = GLUT.loadTexture("textures/brickwall.jpg")
        texture2 = GLUT.loadTexture("textures/awesomeface.png")

        glEnable(GL_DEPTH_TEST)
        glClearColor(Vec4(0.3, 0.4, 0.5, 1.0))
    }

    override fun loop() {
        glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)

        texture1!!.bind(TextureTarget._2D)
        GL13.glActiveTexture(GL13C.GL_TEXTURE0)

        texture2!!.bind(TextureTarget._2D)
        GL13.glActiveTexture(GL13C.GL_TEXTURE1)


        program!!.use {
            vao!!.bind()
            vbo!!.bind(BufferTarget.ARRAY)

            aPosAttr.enable()
            aPosAttr.pointer()

            aUVAttr.enable()
            aUVAttr.pointer()

            glUniform(texture1Location, 0)
            glUniform(texture2Location, 1)

            val radius = 10.0f


            val camX = sin(glfwGetTime().toFloat()) * radius
            val camZ = cos(glfwGetTime().toFloat()) * radius

            val viewMatrix = glm.lookAt(Vec3(camX, 0.0f, camZ), Vec3(0, 0, 0), Vec3(0, 1, 0))
            val projectMatrix = glm.perspective(glm.radians(60.0f), 4.0f / 3.0f, 0.001f, 1000.0f)

            for (i in 0 until cubicPositions.size) {
                var modelMatrix = Mat4(1.0f)
                modelMatrix = glm.translate(modelMatrix, cubicPositions[i])
                val angle = 20.0f * i
                modelMatrix = glm.rotate(modelMatrix, glm.radians(angle), Vec3(1.0f, 0.3f, 0.5f))

                val mvp = projectMatrix * viewMatrix * modelMatrix

                glUniform(mvpLocation, mvp)

                glDrawArrays(DrawMode.TRIANGLES, 36)
            }
        }
    }


    companion object {

        @JvmStatic
        fun main(args: Array<String>) {
            st.learnopengl.apps.basic.Camera3D(1280, 720, "Camera 3D").run()
        }
    }
}